home *** CD-ROM | disk | FTP | other *** search
/ Languguage OS 2 / Languguage OS II Version 10-94 (Knowledge Media)(1994).ISO / gnu / nihcl-30.lha / nihcl-3.0 / lib / Regex.c < prev    next >
C/C++ Source or Header  |  1990-05-19  |  10KB  |  404 lines

  1. /* Regex.c  -- implementation of NIHCL class Regex
  2.  
  3.     THIS SOFTWARE FITS THE DESCRIPTION IN THE U.S. COPYRIGHT ACT OF A
  4.     "UNITED STATES GOVERNMENT WORK".  IT WAS WRITTEN AS A PART OF THE
  5.     AUTHOR'S OFFICIAL DUTIES AS A GOVERNMENT EMPLOYEE.  THIS MEANS IT
  6.     CANNOT BE COPYRIGHTED.  THIS SOFTWARE IS FREELY AVAILABLE TO THE
  7.     PUBLIC FOR USE WITHOUT A COPYRIGHT NOTICE, AND THERE ARE NO
  8.     RESTRICTIONS ON ITS USE, NOW OR SUBSEQUENTLY.
  9.  
  10. Author:
  11.     K. E. Gorlen
  12.     Bg. 12A, Rm. 2033
  13.     Computer Systems Laboratory
  14.     Division of Computer Research and Technology
  15.     National Institutes of Health
  16.     Bethesda, Maryland 20892
  17.     Phone: (301) 496-1111
  18.     uucp: uunet!nih-csl!kgorlen
  19.     Internet: kgorlen@alw.nih.gov
  20.     December, 1987
  21.  
  22. Function:
  23.  
  24. Regex is a class derived from String and containing a regular
  25. expression and its compiled form.  It implements functions that search
  26. Strings for and match Strings with regular expressions using the
  27. regular expression code from GNU Emacs Version 18.41 (regex.c).
  28.  
  29. Note that when a Regex is printed, only the String portion is printed,
  30. not the compiled form.
  31.     
  32. $Log:    Regex.c,v $
  33.  * Revision 3.0  90/05/20  00:21:01  kgorlen
  34.  * Release for 1st edition.
  35.  * 
  36. */
  37.  
  38. #include "Regex.h"
  39. #include "nihclIO.h"
  40. #include <ctype.h>
  41. #include <iomanip.h>
  42. #include <libc.h>
  43.  
  44. #ifdef SYSV
  45.  
  46. #include <memory.h>
  47. inline void bcopy(void* from, void* to, int sz)
  48. {
  49.     memcpy(to,from,sz);
  50. }
  51.  
  52. #endif
  53.  
  54. #define    THIS    Regex
  55. #define    BASE    String
  56. #define BASE_CLASSES BASE::desc()
  57. #define MEMBER_CLASSES
  58. #define VIRTUAL_BASE_CLASSES
  59.  
  60. DEFINE_CLASS(Regex,1,"$Header: /afs/alw.nih.gov/unix/sun4_40c/usr/local/src/nihcl-3.0/share/lib/RCS/Regex.c,v 3.0 90/05/20 00:21:01 kgorlen Rel $",NULL,NULL);
  61.  
  62. const unsigned Regex::DEFAULT_BUFSIZE = 64;
  63.  
  64. extern const int NIHCL_BADREGEX;
  65.  
  66. /*
  67. extern const char* re_compile_pattern(
  68.     const char*,    // the address of the pattern string
  69.     int size,    // the length of the pattern string
  70.     struct re_pattern_buffer* bufp);
  71.  
  72. re_compile_pattern takes a regular-expression descriptor string in the
  73. user's format and converts it into a buffer full of byte commands for
  74. matching.
  75.  
  76.   pattern   is the address of the pattern string
  77.   size      is the length of it.
  78.   bufp        is a  struct re_pattern_buffer *  which points to the info
  79.         on where to store the byte commands.
  80.         This structure contains a  char *  which points to the
  81.         actual space, which should have been obtained with malloc().
  82.         compile_pattern may use realloc() to grow the buffer space.
  83.  
  84. The number of bytes of commands can be found out by looking in the
  85. struct re_pattern_buffer that bufp pointed to, after compile_pattern
  86. returns.
  87. */
  88.  
  89. /*
  90. extern int re_match(struct re_pattern_buffer*, const char*, int size,
  91.     int pos, struct re_registers*);
  92.  
  93. extern int re_match_2(
  94.     struct re_pattern_buffer* pbufp,
  95.     const char* string1, int size1,
  96.     const char* string2, int size2,
  97.     int pos,
  98.     struct re_registers*,
  99.     int mstop);
  100.  
  101. Match the pattern described by `pbufp' against data which is the
  102. virtual concatenation of `string1' and `string2'.  `size1' and `size2'
  103. are the sizes of the two data strings.  Start the match at position
  104. `pos'.  Do not consider matching past the position `mstop'.
  105.  
  106. If pbufp->fastmap is nonzero, then it had better be up to date.
  107.  
  108. The reason that the data to match is specified as two components which
  109. are to be regarded as concatenated is so that this function can be
  110. used directly on the contents of an Emacs buffer.
  111.  
  112. -1 is returned if there is no match.  Otherwise the value is the
  113. length of the substring which was matched.
  114.  
  115. re_match just calls re_match_2 with size1=0 and mstop=size.
  116. */
  117.  
  118. /*
  119. extern int re_search(struct re_pattern_buffer*,    const char*, int size,
  120.     int startpos, int range, struct re_registers*);
  121.  
  122. extern int re_search_2(
  123.     struct re_pattern_buffer*,
  124.     const char*, int size1,
  125.     const char*, int size2,
  126.     int startpos,
  127.     int range,
  128.     struct re_registers*,
  129.     int mstop);
  130.  
  131. Like re_match_2 but tries first a match starting at index `startpos',
  132. then at startpos + 1, and so on.  `range' is the number of places to
  133. try before giving up.  If `range' is negative, the starting positions
  134. tried are startpos, startpos - 1, etc.  It is up to the caller to make
  135. sure that range is not so large as to take the starting position
  136. outside of the input strings.
  137.  
  138. The value returned is the position at which the match was found, or -1
  139. if no match was found.
  140.  
  141. re_search just calls re_search_2 with size1=0 and mstop=size.
  142. */
  143.  
  144. const unsigned BYTEWIDTH = 8;    // width of a byte in bits
  145.  
  146. void Regex::re_compile_pattern()
  147. {
  148.     const char* error = ::re_compile_pattern(*this,length(),&pattern);
  149.     if (error) errRegex(error);
  150. }
  151.  
  152. int Regex::re_match(const String& str, int pos)
  153. {
  154.     int len = ::re_match(&pattern, str, str.length(), pos, ®s);
  155.     setGroups(len);
  156.     return len;
  157. }
  158.  
  159. void Regex::init(int bufsize)
  160. {
  161.     pattern.buffer = (char*)malloc(bufsize);
  162.     pattern.allocated = bufsize;
  163.     pattern.used = 0;
  164.     pattern.fastmap = 0;
  165.     pattern.translate = 0;
  166.     ngroups = 0;
  167.     for (int i=0; i<RE_NREGS; i++) {
  168.         regs.start[i] = regs.end[i] = -1;
  169.     }
  170. }
  171.  
  172. void Regex::fixCopy()
  173. // copy heap storage in a struct re_pattern_buffer
  174. {
  175.     register char* oldp = pattern.buffer;
  176.     pattern.buffer = (char*)malloc(pattern.allocated);
  177.     bcopy(oldp,pattern.buffer,pattern.used);
  178.     if (pattern.fastmap) {
  179.         oldp = pattern.fastmap;
  180.         pattern.fastmap = (char*)malloc(1<<BYTEWIDTH);
  181.         bcopy(oldp,pattern.fastmap,1<<BYTEWIDTH);
  182.     }
  183. }
  184.  
  185. void Regex::setGroups(int result)
  186. // set number of groups matched by last match/search
  187. {
  188.     ngroups = 0;
  189.     if (result == -1) return;
  190.     for (register i=0; i<RE_NREGS; i++) {
  191.         if (regs.start[i] == -1) return;
  192.         ngroups++;
  193.     }
  194. }
  195.  
  196. void Regex::errRegex(const char* s) const
  197. {
  198.     const char* re = *this;
  199.     setError(NIHCL_BADREGEX,DEFAULT,s,this,re);
  200. }
  201.  
  202. Regex::Regex(unsigned bufsize)
  203. {
  204.     init(bufsize);
  205. }
  206.  
  207. Regex::Regex(const char* rexp, unsigned bufsize) : BASE(rexp)
  208. {
  209.     init(bufsize);
  210.     re_compile_pattern();
  211. }
  212.  
  213. Regex::Regex(const Regex& rexp) : BASE(rexp)
  214. {
  215. // A copy is more efficient than re_compile_pattern()
  216.     pattern = rexp.pattern;
  217.     regs = rexp.regs;
  218.     fixCopy();
  219. }
  220.  
  221. Regex::~Regex()
  222. {
  223.     free(pattern.buffer);
  224.     if (pattern.fastmap) free(pattern.fastmap);
  225. }
  226.  
  227. Range Regex::operator[](unsigned i) const
  228. {
  229.     if (i >= ngroups) return Range(0,-1);    // should raise exception
  230.     return Range(regs.start[i],regs.end[i]-regs.start[i]);
  231. }
  232.  
  233. void Regex::operator=(const char* cs)
  234. {
  235.     *(String*)this=cs;
  236.     re_compile_pattern();
  237. }
  238.  
  239. void Regex::operator=(const Regex& rexp)
  240. {
  241.     if (this == &rexp) return;    // watch out for x = x
  242.     *(String*)this=rexp;        // copy the String part
  243.     free(pattern.buffer);
  244.     pattern = rexp.pattern;
  245.     regs = rexp.regs;
  246.     fixCopy();
  247. }
  248.  
  249. void Regex::operator=(const String& str)
  250. {
  251.     *(String*)this = str;
  252.     re_compile_pattern();
  253. }
  254.  
  255. void Regex::operator=(const SubString& substr)
  256. {
  257.     *(String*)this = substr;
  258.     re_compile_pattern();
  259. }
  260.  
  261. void Regex::deepenShallowCopy()
  262. // Called by deepCopy() to convert a shallow copy to a deep copy.
  263. {
  264.     BASE::deepenShallowCopy();
  265.     fixCopy();
  266. }
  267.  
  268. void Regex::scanFrom(istream& strm)
  269. {
  270.     String::scanFrom(strm);
  271.     re_compile_pattern();
  272. }
  273.  
  274. void Regex::toAscii()
  275. {
  276.     String::toAscii();
  277.     re_compile_pattern();
  278. }
  279.  
  280. void Regex::toLower()
  281. {
  282.     String::toLower();
  283.     re_compile_pattern();
  284. }
  285.  
  286. void Regex::toUpper()
  287. {
  288.     String::toUpper();
  289.     re_compile_pattern();
  290. }
  291.  
  292. void Regex::storer(OIOout& strm) const
  293. {
  294.     BASE::storer(strm);
  295.     strm << pattern.allocated << ngroups;
  296.     for (int i=0; i<ngroups; i++) {
  297.         strm << regs.start[i] << regs.end[i];
  298.     }
  299. }
  300.  
  301. Regex::Regex(OIOin& strm)
  302. :
  303. #ifdef MI
  304.     Object(strm),
  305. #endif
  306.     BASE(strm)    
  307. {
  308.     int bufsize;
  309.     strm >> bufsize;
  310.     init(bufsize);
  311.     re_compile_pattern();
  312.     strm >> ngroups;
  313.     for (int i=0; i<ngroups; i++) {
  314.         strm >> regs.start[i] >> regs.end[i];
  315.     }
  316. }
  317.  
  318. void Regex::storer(OIOofd& fd) const
  319. {
  320.     BASE::storer(fd);
  321.     fd << pattern.allocated;
  322.     fd << ngroups;
  323.     for (int i=0; i<ngroups; i++) {
  324.         fd << regs.start[i];
  325.         fd << regs.end[i];
  326.     }
  327. }
  328.  
  329. Regex::Regex(OIOifd& fd)
  330. :
  331. #ifdef MI
  332.     Object(fd),
  333. #endif
  334.     BASE(fd)    
  335. {
  336.     int bufsize;
  337.     fd >> bufsize;
  338.     init(bufsize);
  339.     re_compile_pattern();
  340.     fd >> ngroups;
  341.     for (int i=0; i<ngroups; i++) {
  342.         fd >> regs.start[i];
  343.         fd >> regs.end[i];
  344.     }
  345. }
  346.  
  347. bool Regex::match(const String& s, int pos)
  348. // Check for match of Regex at index pos of String s.
  349. {
  350.     return re_match(s,pos) != -1;
  351. }        
  352.  
  353. int Regex::search(const String& str, int startpos)
  354. // Search for next match of Regex in str beginning at startpos.
  355. // Return position of match or -1 if no match.
  356. {
  357.     return search(str,startpos,str.length()-startpos);
  358. }
  359.  
  360. int Regex::search(const String& str, int startpos, int range)
  361. // Search for next match of Regex in str beginning at startpos.
  362. // Try no more than abs(range) times.
  363. // Search backwards if range < 0.
  364. // Return position of match or -1 if no match.
  365. {
  366.     if (!pattern.fastmap) pattern.fastmap = (char*)malloc(1 << BYTEWIDTH);
  367.     int pos = ::re_search(&pattern, str, str.length(), startpos, range, ®s);
  368.     setGroups(pos);
  369.     return pos;
  370. }
  371.  
  372. static void printByte(ostream& strm, char b)
  373. {
  374.     if (isprint(b)) {
  375.         if (b == '\\') strm << '\\';
  376.         strm.put(b);
  377.     }
  378.      else strm << "\\" << setfill('0') << setw(3) << oct << (b&0377);
  379. }
  380.  
  381. void Regex::dumpOn(ostream& strm) const
  382. {
  383.     strm << "regular expression: " << *this << '\n';
  384.     strm << "compiled form: ";
  385.     for (int i=0; i<pattern.used; i++) printByte(strm,pattern.buffer[i]);
  386.     strm << '\n';
  387.     strm << "allocated: " << pattern.allocated << '\n';
  388.     strm << "used: " << pattern.used << '\n';
  389.     if (pattern.fastmap) {
  390.         strm << "fastmap: ";
  391.         for (i=0; i < (1 << BYTEWIDTH); i++) {
  392.             if (pattern.fastmap[i]) printByte(strm,i);
  393.         }
  394.         strm << '\n';
  395.     }
  396.     strm << "fastmap accurate: " << (pattern.fastmap_accurate ? "YES" : "NO") << '\n';
  397.     strm << "match null string: " << (pattern.can_be_null ? "YES" : "NO") << '\n';
  398.     strm << "groups: " << groups() << '\n';
  399.     for (i=0; i<RE_NREGS; i++) {
  400.         if (regs.start[i] == -1) break;
  401.         strm << "regs[" << i << "]:    " << regs.start[i] << ',' << regs.end[i] << '\n';
  402.     }
  403. }
  404.